package cn.xiuxius.askbox.service.impl;

import cn.hutool.crypto.SecureUtil;
import cn.xiuxius.askbox.common.constant.AccountState;
import cn.xiuxius.askbox.common.constant.RedisConstant;
import cn.xiuxius.askbox.common.exception.AccountAlreadyExistsException;
import cn.xiuxius.askbox.common.exception.AccountNotFoundException;
import cn.xiuxius.askbox.common.exception.CaptchaInvalidException;
import cn.xiuxius.askbox.common.exception.LoginFailedException;
import cn.xiuxius.askbox.common.utils.JwtUtil;
import cn.xiuxius.askbox.mapper.AdminMapper;
import cn.xiuxius.askbox.pojo.dto.add.AdminAddDTO;
import cn.xiuxius.askbox.pojo.dto.common.LoginDTO;
import cn.xiuxius.askbox.pojo.dto.query.AdminQueryDTO;
import cn.xiuxius.askbox.pojo.dto.update.AdminUpdateDTO;
import cn.xiuxius.askbox.pojo.dto.update.AdminUpdateStateDTO;
import cn.xiuxius.askbox.pojo.entity.AdminEntity;
import cn.xiuxius.askbox.pojo.result.PageResult;
import cn.xiuxius.askbox.pojo.vo.AdminVO;
import cn.xiuxius.askbox.pojo.vo.LoginVO;
import cn.xiuxius.askbox.properties.JwtProperties;
import cn.xiuxius.askbox.service.IConsoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class ConsoleServiceImpl implements IConsoleService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private JwtProperties jwtProperties;

    @Override
    public LoginVO login(LoginDTO dto) {

        verifyCaptcha(dto.getCaptchaKey(), dto.getCaptcha());

        AdminEntity adminEntity = adminMapper.selectOne(Wrappers.<AdminEntity>lambdaQuery()
                .eq(AdminEntity::getUsername, dto.getUsername())
        );

        if (adminEntity == null) {
            throw new AccountNotFoundException("用户不存在");
        }

        if (!adminEntity.getState().equals(AccountState.ALIVE)) {
            throw new LoginFailedException("账户状态异常");
        }

        String password = SecureUtil.sha256(dto.getPassword());

        if (!adminEntity.getPassword().equals(password)) {
            throw new LoginFailedException("密码错误");
        }

        Map<String, Object> claims = new HashMap<>();
        claims.put("username", adminEntity.getUsername());
        claims.put("uuid", adminEntity.getUuid());

        String token = JwtUtil.createJWT(jwtProperties.getConsoleSecret(), jwtProperties.getConsoleExpire(), claims);

        return LoginVO.builder()
                .uuid(adminEntity.getUuid())
                .token(token)
                .build();
    }

    @Override
    public void addAdmin(AdminAddDTO dto) {

        AdminEntity adminEntity = adminMapper.selectOne(Wrappers.<AdminEntity>lambdaQuery()
                .eq(AdminEntity::getUsername, dto.getUsername())
        );

        if (adminEntity != null) {
            throw new AccountAlreadyExistsException("用户名已存在");
        }

        adminEntity = new AdminEntity();

        BeanUtils.copyProperties(dto, adminEntity);

        adminEntity.setUuid(UUID.randomUUID().toString());

        adminEntity.setPassword(SecureUtil.sha256(dto.getPassword()));

        adminMapper.insert(adminEntity);

    }

    @Override
    public PageResult getAdminsByPage(AdminQueryDTO queryDTO) {
        LambdaQueryWrapper<AdminEntity> wrapper = Wrappers.<AdminEntity>lambdaQuery()
                .like(StringUtils.isNotBlank(queryDTO.getUsername()), AdminEntity::getUsername, queryDTO.getUsername())
                .like(StringUtils.isNotBlank(queryDTO.getDisplayName()), AdminEntity::getDisplayName, queryDTO.getDisplayName())
                .like(StringUtils.isNotBlank(queryDTO.getEmail()), AdminEntity::getEmail, queryDTO.getEmail())
                .like(StringUtils.isNotBlank(queryDTO.getPhone()), AdminEntity::getPhone, queryDTO.getPhone())
                .like(StringUtils.isNotBlank(queryDTO.getUuid()), AdminEntity::getUuid, queryDTO.getUuid())
                .eq(AdminEntity::getState, queryDTO.getState())
                .between(Objects.nonNull(queryDTO.getCreateTimeBegin()) && Objects.nonNull(queryDTO.getCreateTimeEnd()), AdminEntity::getCreateTime, queryDTO.getCreateTimeBegin(), queryDTO.getCreateTimeEnd())
                .orderByDesc(AdminEntity::getUpdateTime);

        Page<AdminEntity> pageResult = adminMapper.selectPage(new Page<>(queryDTO.getPage(), queryDTO.getPageSize()), wrapper);

        List<AdminVO> vos = new LinkedList<>();

        for (AdminEntity record : pageResult.getRecords()) {
            AdminVO vo = new AdminVO();
            BeanUtils.copyProperties(record, vo);
            vos.add(vo);
        }

        return new PageResult(pageResult.getTotal(), vos);
    }

    @Override
    public AdminVO getAdminByUuid(AdminQueryDTO dto) {
        AdminEntity adminEntity = adminMapper.selectOne(Wrappers.<AdminEntity>lambdaQuery().eq(AdminEntity::getUuid, dto.getUuid()));
        AdminVO vo = new AdminVO();
        BeanUtils.copyProperties(adminEntity, vo);
        return vo;
    }

    @Override
    public void updateAdmin(AdminUpdateDTO dto) {
        AdminEntity adminEntity = adminMapper.selectOne(Wrappers.<AdminEntity>lambdaQuery()
                .eq(AdminEntity::getUsername, dto.getUsername())
        );

        if (adminEntity != null && !adminEntity.getUuid().equals(dto.getUuid())) {
            throw new AccountAlreadyExistsException("用户名已存在");
        }

        if (adminEntity != null) {
            BeanUtils.copyProperties(dto, adminEntity);
        }
        adminMapper.update(adminEntity, Wrappers.<AdminEntity>lambdaUpdate()
                .eq(AdminEntity::getUuid, dto.getUuid())
                .set(StringUtils.isNotBlank(dto.getPassword()), AdminEntity::getPassword, SecureUtil.sha256(dto.getPassword()))
                .set(StringUtils.isNotBlank(dto.getDisplayName()), AdminEntity::getDisplayName, dto.getDisplayName())
                .set(StringUtils.isNotBlank(dto.getEmail()), AdminEntity::getEmail, dto.getEmail())
                .set(StringUtils.isNotBlank(dto.getPhone()), AdminEntity::getPhone, dto.getPhone())
                .set(StringUtils.isNotBlank(dto.getUsername()), AdminEntity::getUsername, dto.getUsername())
                .set(StringUtils.isNotBlank(dto.getAvatar()), AdminEntity::getAvatar, dto.getAvatar())
        );
    }

    @Override
    public void updateAdminState(AdminUpdateStateDTO dto) {
        AdminEntity adminEntity = new AdminEntity();
        BeanUtils.copyProperties(dto, adminEntity);
        adminMapper.update(adminEntity, Wrappers.<AdminEntity>lambdaUpdate()
                .eq(AdminEntity::getUuid, dto.getUuid())
                .set(Objects.nonNull(dto.getState()), AdminEntity::getState, dto.getState())
        );
    }

    @Override
    public void deleteAdmin(String uuid) {
        adminMapper.delete(Wrappers.<AdminEntity>lambdaQuery().eq(AdminEntity::getUuid, uuid));
    }

    private void verifyCaptcha(String captchaKey, String captcha) {
        Object result = redisTemplate.opsForValue().get(RedisConstant.LOGIN_CAPTCHA_KEY + captchaKey);
        if (result == null) {
            throw new CaptchaInvalidException("验证码已失效");
        }
        Double resultDouble = (Double) result;
        Double captchaDouble = Double.valueOf(captcha);

        if (!resultDouble.equals(captchaDouble)) {
            throw new CaptchaInvalidException("验证码错误");
        }
        redisTemplate.delete(RedisConstant.LOGIN_CAPTCHA_KEY + captchaKey);
    }

}
